﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using 历史记录.DataAccessLayer;
using 历史记录.Helper;

namespace 历史记录.BusinessLogicLayer
{
    /// <summary>
    /// 分表存储
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SubmeterBll<T> : DisposableBase, IBLL<T>
     where T : Model, new()
    {
        public IDAL<T> DAL { get; }

        /// <summary>
        /// 保存天数默认保存五天
        /// </summary>
        public TimeSpan SaveSpan { get; }

        /// <summary>
        /// 最早记录时间
        /// </summary>
        public DateTime Start { get; private set; }

        /// <summary>
        /// 最晚记录时间
        /// </summary>
        public DateTime End { get; private set; }

        /// <summary>
        /// 删除间隔
        /// </summary>
        public TimeSpan DeleteSpan { get; }

        private HashSet<string> Names { get; }

        internal SubmeterBll(IDAL<T> dal, TimeSpan span, TimeSpan delete)
        {
            DAL = dal;
            SaveSpan = span;
            DeleteSpan = delete;
            Names = new HashSet<string>(DAL.GetTableList().Where(a => a.IsTableName()).OrderBy(a => a));
            if (Names.Count > 0)
            {
                Start = DAL.Min(Names.Min());
                End = DAL.Max(Names.Max());
            }
        }

        private void InitTables(string table)
        {
            if (Names.Add(table))
                DAL.InitTables(table);
        }

        public bool Insertable(List<T> insertObjs)
        {
            if (insertObjs == null || insertObjs.Count == 0) return false;
            End = insertObjs.Last().Id;
            var table = End.ToTableName();
            InitTables(table);
            return DAL.Insertable(table, insertObjs);
        }

        public bool UpDate(DateTime date, List<T> data)
        {
            bool every = false;
            if (date.Date != End.Date)
            {
                Insertable(data);
                every = true;
            }
            var span = date - Start;
            if (span > DeleteSpan && span > SaveSpan)
            {
                var start = date - SaveSpan;
                var name = start.ToTableName();
                var names = Names.Where(a => name.CompareTo(a) >= 0);
                foreach (var item in names)
                {
                    if (name == item)
                        DAL.DeleteMany(item, a => a.Id < start);
                    else if (Names.Remove(item))
                        DAL.DropTable(item);
                }
                Start = start;
            }
            return every;
        }

        public IEnumerable<IEnumerable<TResult>> Queryable<TResult>(DateTime start, DateTime end, Expression<Func<T, TResult>> Select, Expression<Func<T, bool>> Where = null) where TResult : new()
        {
            var length = (end - start).Days;
            for (int i = 0; i <= length; i++)
            {
                var date = start.AddDays(i);
                var expression = Where;
                if (i == 0)
                    expression = expression.AndAlso(a => a.Id >= start);
                else if (i == length)
                    expression = expression.AndAlso(a => a.Id <= end);
                yield return DAL.Queryable(date.ToTableName(), Select, expression);
            }
        }

        public IEnumerable<IEnumerable<T>> Queryable(DateTime start, DateTime end, Expression<Func<T, bool>> Where = null)
        {
            var length = (end - start).Days;
            for (int i = 0; i <= length; i++)
            {
                var date = start.AddDays(i);
                var expression = Where;
                if (i == 0)
                    expression = expression.AndAlso(a => a.Id >= start);
                else if (i == length)
                    expression = expression.AndAlso(a => a.Id <= end);
                yield return DAL.Queryable(date.ToTableName(), expression);
            }
        }

        protected override void OnDispose()
        {
            DAL.Dispose();
        }
    }
}